package com.cnswhy.cloud.common.datasource.handler;

import com.cnswhy.cloud.common.core.exception.BizException;
import com.cnswhy.cloud.common.datasource.utils.AESUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Base64;

public class AESTypeHandler extends BaseTypeHandler<String> {

    private static Key aesKey;
    private static IvParameterSpec aesIv;
    static {
        //crypto.aes.secret=zmLPmHKLor6SvWqrIXQvhmIWP66l3+wIhhwWElP9VYc=
        //crypto.aes.iv=
        //
        String keyBase64 = "zmLPmHKLor6SvWqrIXQvhmIWP66l3+wIhhwWElP9VYc=";
        String ivString = "cnswhy.2020!";
        if (!StringUtils.isEmpty(keyBase64)) {
            byte[] raw = Base64.getDecoder().decode(keyBase64);
            if (raw.length != 0) {
                aesKey = new SecretKeySpec(raw, "AES");
            }

            if (!StringUtils.isEmpty(ivString)) {
                aesIv = new IvParameterSpec(ivString.getBytes());
            }
        }


    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        if (aesKey == null) throw new BizException( "missing property [crypto.aes.secret]");
        if (aesIv == null) throw new BizException( "missing property [crypto.aes.iv]");

        try {
            byte[] bytes = AESUtils.encrypt(aesKey, parameter.getBytes(StandardCharsets.UTF_8), "AES/CBC/PKCS5Padding", aesIv);
            String data = Base64.getEncoder().encodeToString(bytes);
            ps.setString(i, data);
        } catch (Exception e) {
            throw new BizException( "something wrong happened while decrypting data");
        }
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String str = rs.getString(columnName);
        if (rs.wasNull())
            return null;

        return decryptContent(str);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String str = rs.getString(columnIndex);
        if (rs.wasNull())
            return null;

        return decryptContent(str);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String str = cs.getString(columnIndex);
        if (cs.wasNull())
            return null;

        return decryptContent(str);
    }

    private String decryptContent(String base64) {
        if (aesKey == null) throw new BizException( "missing property [crypto.aes.secret]");
        if (aesIv == null) throw new BizException( "missing property [crypto.aes.iv]");

        byte[] bytes = Base64.getDecoder().decode(base64);
        try {
            byte[] contentBytes = AESUtils.decrypt(aesKey, bytes, "AES/CBC/PKCS5Padding", aesIv);
//            byte[] contentBytes = AESUtils.decrypt(aesKey, bytes);
            return new String(contentBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new BizException( "something wrong happened while decrypting data");
        }
    }
}
